package com.fsquare.inventory.service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.catalog.service.CatalogService;
import org.broadleafcommerce.core.inventory.service.type.InventoryType;
import org.springframework.transaction.annotation.Transactional;

import com.fsquare.inventory.dao.SkuInventoryDao;
import com.fsquare.inventory.dao.SkuMovementDao;
import com.fsquare.inventory.domain.SkuInventory;
import com.fsquare.inventory.domain.SkuMovement;
import com.fsquare.inventory.domain.SkuMovement.MovementType;

public class InventoryServiceImpl implements InventoryService{

	private static final Log LOG = LogFactory.getLog(InventoryServiceImpl.class);

	
	@Resource(name = "skuInventoryDao")
    protected SkuInventoryDao skuInventoryDao;
	
	@Resource(name = "skuMovementDao")
    protected SkuMovementDao skuMovementDao;
	
	@Resource(name = "blCatalogService")
    protected CatalogService catalogService;
	
	
	@Override
	@Transactional("blTransactionManager")
	public List<SkuMovement> getSkuMovements(Sku sku) {
		return skuMovementDao.getSkuMovements(sku);
	}
	
	@Override
	public List<SkuMovement> getSkuMovements(Long skuId) {
		return skuMovementDao.getSkuMovements(skuId);
	}

	@Override
	@Transactional("blTransactionManager")
	public SkuMovement saveMovement(SkuMovement skuMovement) {
		skuMovement.setUpdateDate(new Date());
		return skuMovementDao.save(skuMovement);
	}

	@Override
	@Transactional("blTransactionManager")
	public SkuInventory getInventory(Sku sku) {
		return skuInventoryDao.getInventory(sku.getId());
	}
	
	@Override
	@Transactional("blTransactionManager")
	public SkuInventory getInventory(Long skuId) {
		SkuInventory skuInventory = skuInventoryDao.getInventory(skuId);
		if(skuInventory == null){
			skuInventory = createNewSkuInventory(skuId);
		}
			
		return skuInventory;
	}

	@Override
	@Transactional("blTransactionManager")
	public SkuInventory saveInventory(SkuInventory skuInventory) {
		return skuInventoryDao.save(skuInventory);
	}

	@Override
	@Transactional("blTransactionManager")
	public boolean isAvailable(Long skuId, Integer quantity) {
		if (quantity < 1) {
            throw new IllegalArgumentException("Quantity " + quantity + " is not valid. Must be greater than zero.");
	    }
		SkuInventory inventory =  getInventory(skuId);
		Integer quantityAvailable = inventory.getQuantityAvailable();
		return (quantity >= quantityAvailable);
	}

	@Override
	@Transactional("blTransactionManager")
	public Integer retrieveQuantityAvailable(Long skuId) {
		SkuInventory inventory =  getInventory(skuId);
		return inventory.getQuantityAvailable();
	}

	@Override
	public boolean isAvailable(SkuInventory inventory, Integer quantity) {
		Integer quantityAvailable = inventory.getQuantityAvailable();
		return (quantity >= quantityAvailable);
	}
	
	

    protected boolean checkBasicAvailablility(Sku sku) {
        Boolean available = sku.isAvailable();
        if (available == null) {
            available = true;
        }
        if (sku != null && available && sku.isActive()) {
            return true;
        }
        return false;
    }
	
    @Override
    @Transactional(value = "blTransactionManager")
    public SkuInventory updateInventory(SkuInventory inventory, Integer updateQuantity, MovementType movementType)throws InventoryUnavailableException{
    	Integer inventoryAvailable = inventory.getQuantityAvailable();
    	Integer newInventory = inventoryAvailable;
    	if(MovementType.DECREMENT.equals(movementType)){
    		if (inventoryAvailable < updateQuantity) {
                throw new InventoryUnavailableException("There was not enough inventory to fulfill this request.", inventory.getSku().getId(), updateQuantity, inventoryAvailable);
    		}
                newInventory = inventoryAvailable - updateQuantity;
    	}else
    		newInventory = inventoryAvailable + updateQuantity;
    	
    	inventory.setQuantityAvailable(newInventory);

    	SkuMovement skuMovement = skuMovementDao.createSkuMovement();
    	skuMovement.setQuantity(updateQuantity);
    	skuMovement.setMovementType(movementType);
    	skuMovement.setSku(inventory.getSku());
    	skuMovement = saveMovement(skuMovement);
    	
    	inventory.setMovement(skuMovement);
    	
    	inventory = saveInventory(inventory);
    	
    	return inventory;
    }
	
    @Override
    @Transactional(value = "blTransactionManager")
    public SkuInventory createNewSkuInventory(Long skuId){
    	return createNewSkuInventory(skuId, 0);
    }
    
    @Override
    @Transactional(value = "blTransactionManager")
    public SkuInventory createNewSkuInventory(Long skuId, Integer quatity){
    	SkuInventory inventory = skuInventoryDao.createSkuInventory();
    	Sku sku = catalogService.findSkuById(skuId);
    	inventory.setSku(sku);
    	try {
			return updateInventory(inventory, quatity, MovementType.INCREMENT);
		} catch (InventoryUnavailableException e) {
			e.printStackTrace();
		}
    	return null;
    }
    
    
	@Override
    @Transactional(value = "blTransactionManager", rollbackFor = { InventoryUnavailableException.class })
    public SkuInventory decrementInventory(Long skuId, int quantity) throws InventoryUnavailableException {
        if (quantity < 1) {
            throw new IllegalArgumentException("Quantity " + quantity + " is not valid. Must be greater than zero.");
        }

        Sku sku = null;
        SkuInventory inventory =  getInventory(skuId);
        if(inventory == null){
        	createNewSkuInventory(skuId);
        	throw new InventoryUnavailableException("There was not enough inventory to fulfill this request.", sku.getId(), quantity, 0);
        }else
        	sku = inventory.getSku();
        
        
        if (checkBasicAvailablility(sku) && (sku.getInventoryType() == null || InventoryType.BASIC.equals(sku.getInventoryType()))) {

            Integer inventoryAvailable = inventory.getQuantityAvailable();
            if (inventoryAvailable == null) {
                return inventory;
            }
            if (inventoryAvailable < quantity) {
                throw new InventoryUnavailableException(
                        "There was not enough inventory to fulfill this request.", skuId, quantity, inventoryAvailable);
            }

            inventory = updateInventory(inventory, quantity, MovementType.DECREMENT);

        }
        
        return inventory;
    }

    @Override
    @Transactional(value = "blTransactionManager", rollbackFor = { InventoryUnavailableException.class })
    public void decrementInventory(Map<Long, Integer> skuQuantities) throws InventoryUnavailableException {
        Set<Long> keys = skuQuantities.keySet();
        for (Long key : keys) {
            Integer quantity = skuQuantities.get(key);
            if (quantity == null) {
                throw new IllegalArgumentException("Quantity was null for skuId " + key);
            }
            decrementInventory(key, quantity);
        }
    }

    @Override
    @Transactional("blTransactionManager")
    public SkuInventory incrementInventory(Long skuId, int quantity) {
        if (quantity < 1) {
            throw new IllegalArgumentException("Quantity " + quantity + " is not valid. Must be greater than zero.");
        }

        Sku sku = null;
        SkuInventory inventory =  getInventory(skuId);
        if(inventory == null){
			createNewSkuInventory(skuId, quantity);
		
        }else{
        	sku = inventory.getSku();
        
	        if (sku.getInventoryType() == null || InventoryType.BASIC.equals(sku.getInventoryType())) {

	            try {
					inventory = updateInventory(inventory, quantity, MovementType.INCREMENT);
				} catch (InventoryUnavailableException e) {
					e.printStackTrace();
				}
	            
	        }
        }
        
        return inventory;
    }

    @Override
    @Transactional("blTransactionManager")
    public void incrementInventory(Map<Long, Integer> skuQuantities) {
        Set<Long> keys = skuQuantities.keySet();
        for (Long key : keys) {
            Integer quantity = skuQuantities.get(key);
            if (quantity == null) {
                throw new IllegalArgumentException("Quantity was null for skuId " + key);
            }
            incrementInventory(key, quantity);
        }
    }
	
	
	public SkuInventoryDao getSkuInventoryDao() {
		return skuInventoryDao;
	}

	public void setSkuInventoryDao(SkuInventoryDao skuInventoryDao) {
		this.skuInventoryDao = skuInventoryDao;
	}

	public SkuMovementDao getSkuMovementDao() {
		return skuMovementDao;
	}

	public void setSkuMovementDao(SkuMovementDao skuMovementDao) {
		this.skuMovementDao = skuMovementDao;
	}


}
